package job1.server;


//一、基于Zookeeper实现简易版配置中心要求实现以下功能：
//1. 创建一个Web项目，将数据库连接信息交给Zookeeper配置中心管理，即：当项目Web项目启动时，从Zookeeper进行MySQL配置参数的拉取
//2. 要求项目通过数据库连接池访问MySQL（连接池可以自由选择熟悉的）
//3. 当Zookeeper配置信息变化后Web项目自动感知，正确释放之前连接池，创建新的连接池

import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class WebServer {
    DruidDataConnPool connPool;
    //   储存在zookeeper上的mysql配置文件路径
    String mysqlConfZkPath = "/mysql_conf";
    //  zk 客户端
    ZkClient zkClient;

    public WebServer() throws Exception {
//        创建zk客户端
        zkClient = new ZkClient("node1:2181,node2:2181");
//        根据在zk上的路径读取mysql配置
        byte[] confBytes = zkClient.readData(mysqlConfZkPath);
        System.out.println("读取到的mysql配置为\n" + new String(confBytes, StandardCharsets.UTF_8));
//        替换连接池
        replaceConnPool(confBytes);

//        监听zk下该目录mysql 配置文件内容的变化
        zkClient.subscribeDataChanges(mysqlConfZkPath, new IZkDataListener() {
            @Override
            public void handleDataChange(String path, Object data) throws Exception {
                System.out.println("检测到mysql配置发生变化，使用最新mysql配置\n" +new String((byte[]) data, StandardCharsets.UTF_8));
//               使用最新配置替换连接池
                replaceConnPool((byte[]) data);
            }
            @Override
            public void handleDataDeleted(String s) {

            }
        });
    }

    //  更新连接池
    private synchronized void replaceConnPool(byte[] confBytes) throws Exception {
//       正确释放之前使用过的连接池
        if (connPool != null) {
            connPool.destroy();
        }
//        将confBytes转换成德鲁伊连接池所需要的Properties对象
        Properties properties = new Properties();
        properties.load(new ByteArrayInputStream(confBytes));
//        根据zk下载到的配置创建德鲁伊连接池
        connPool = new DruidDataConnPool(properties);
        System.out.println("已正确应用新的mysql配置创建连接池");
    }

//    添加同步代码，防止在使用旧连接池的同时，释放久的连接池
    public synchronized void selectDemo() throws SQLException {
        QueryRunner queryRunner = new QueryRunner(connPool.getDataSource());
//    从数据库查询user表数据
        List<Map<String, Object>> query = queryRunner.query("select host,user from user", new MapListHandler());
//        打印查询结果
        System.out.println(query);
    }

    public static void main(String[] args) throws Exception {
//        创建webServer对象
        WebServer webServer = new WebServer();
        while (true) {
            Thread.sleep(2000);
            //每隔两秒，执行一次数据库查询
            webServer.selectDemo();
        }

    }
}


